<?php
// TODO: implement the cache
final class Zeus_Core_Model_Config extends ZeusLib_Object
{

	/**
	 * Define zeus config core file name in the path
	 * PATH_APP_ETC
	 *
	 * @var string
	 */
	CONST ZEUS_CONFIG_FILE_NAMES = 'config.xml';

	/**
	 * Module objects cache
	 *
	 * @var array(Zeus_Core_Model_Config_Modules)
	 */
	protected $uniqueNamesCache = array();


	/**
	 * Loads the configuration,
	 * checks module dependencies,
	 * loads the module configuration.
	 *
	 * @param bool $ignoreCache
	 */
	public function load($ignoreCache = false)
	{
		// Check that the config object is not chached
		if($ignoreCache || !$this->isCached())
		{
			// Get the xml collection
			$xmlCollection = new ZeusLib_Resource_Xml_Collection();

			// Assign the config xml collections to the objects data
			$this->setCollection($xmlCollection);

			// Load default configuration
			$this->loadConfigurationFiles();

			// Load module configuration
			$this->loadModuleConfigurationFiles();
		}
	}

	/**
	 * Returns the option for the given xml path as
	 * xml element.
	 *
	 * @param string $key
	 * @return ZeusLib_Resource_Xml_Collection_Element|null
	 */
	public function getOption($key)
	{
		// Return the option for the key
		return $this->getCollection()->find($key);
	}

	/**
	 * Returns the module option for the given xml path as
	 * xml element.
	 *
	 * @param string $key
	 * @return ZeusLib_Resource_Xml_Collection_Element|null
	 */
	public function getModuleOption($key)
	{
		// Prepare the module key
		// TODO: this souldnt be needed?! because we use the unique name for config entries
		$key = $this->prepareKey($key);

		// Return the module option for the key
		return $this->getCollection()->find($key);
	}

	/**
	 * Retrieves the module name for the unique module name.
	 * As an example if you use this function with the parameter 'blog',
	 * you will get 'Zeus_Blog'.<br><br>
	 *
	 * This is especially used when retrieving classes by unique module name
	 * which is done when using a block for example.
	 *
	 * @param string $uniqueName
	 * @return boolean
	 */
	public function getModuleName($uniqueName)
	{
		// Return the unique name
		return isset($this->uniqueNamesCache[$uniqueName]) ? $this->uniqueNamesCache[$uniqueName] : null;
	}

	/**
	 * Return an array containing all modules with the key as the unique module name and the value the module name.<br><br>
	 *
	 * For example:
	 * <pre>
	 * array(2)
	 * {
	 *   &nbsp;&nbsp;["blog"]=>
	 *   &nbsp;&nbsp;string(9) "Zeus_Blog"
	 *   &nbsp;&nbsp;["core"]=>
	 *   &nbsp;&nbsp;string(9) "Zeus_Core"
	 * }
	 * </pre>
	 *
	 * @return array
	 */
	public function getAllModules()
	{
		// Create an output cache
		$output = array();

		// Iterate all module children
		foreach($this->getCollection()->find('config/modules') as $module)
		{
			// Cache the data
			$output[$module->find('uniqueName')->getValue()] = $module->getName();
		}

		// Return the output cache
		return $output;
	}

	/**
	 * Replaces the unique module name at the beggining of the given key separated
	 * by the given separator (default a slash) by the full module name.
	 *
	 * @param string $key
	 * @param string $moduleSeparator
	 * @return string
	 */
	public function prepareKey($key, $moduleSeparator = '/')
	{
		// Split the key by the slash
		$keySplit = explode($moduleSeparator, $key);

		// Get the module name for the unique name
		$moduleName = $this->getModuleName($keySplit[0]);

		// Unset the unique key to implode later
		unset($keySplit[0]);

		// Concatelate the key with the rest of the key split
		$key = $moduleName . implode($moduleSeparator, $keySplit);

		// Return the result
		return $key;
	}

	/**
	 * This method loads the basic configuration xml files into the xml collection object.<br>
	 * That contains the main configuration file and the module configuration files used to register a module.
	 */
	private function loadConfigurationFiles()
	{
		// Add the zeus core config file
		$this->getCollection()->addFile(PATH_APP_ETC.self::ZEUS_CONFIG_FILE_NAMES);

		// Iterate over the modules directory
		foreach(ZeusLib_Helper_File::scanDir(PATH_APP_ETC_MODULES) as $path)
		{
			// Check whether the path is a xml file
			if(ZeusLib_Helper_File::isXmlFile(PATH_APP_ETC_MODULES.$path))
			{
				// Add config file
				$this->getCollection()->addFile(PATH_APP_ETC_MODULES.$path);
			}
		}

		// Load the files
		$this->getCollection()->load();
	}

	/**
	 * This method loads the module configuration xml files into the xml collection object.<br>
	 * The module configuration files are inside the etc directory of a module directory.
	 */
	private function loadModuleConfigurationFiles()
	{
		// Iterate all module children
		foreach($this->getCollection()->find('config/modules') as $module)
		{
			// Check whether the module is active
			if($module->find('active')->asBoolean())
			{
				// Dependencies achieved flag
				$dependenciesAchieved = true;

				// Check whether the module has dependencies
				if($module->find('depends') !== null)
				{
					// Iterate all dependencies
					foreach($module->find('depends') as $dependency)
					{
						// Check whether the module is not active
						if(!$this->getCollection()->find('config/modules/'.$dependency->getName().'/active')->asBoolean())
						{
							// Update the dependencies achieved flag
							$dependenciesAchieved = false;
						}
					}
				}

				// Check whether the dependencies were achieved
				if($dependenciesAchieved)
				{
					// Cache the unique name
					$this->uniqueNamesCache[$module->find('uniqueName')->getValue()] = $module->getName();

					// Get the path
					$path = DS.classnameToPath($module->getName(), false).DS.'etc'.DS.self::ZEUS_CONFIG_FILE_NAMES;

					// Check whether the file exists in the local codepool
					if(ZeusLib_Helper_File::isXmlFile(PATH_APP_CODE.'local'.$path))
					{
						// Add the xml file
						$this->getCollection()->addFile(PATH_APP_CODE.'local'.$path);
					}
					// Else check whether the file exists in the modules codepool
					elseif(ZeusLib_Helper_File::isXmlFile(PATH_APP_CODE.$module->find('codePool')->getValue().$path))
					{
						// Add the xml file
						$this->getCollection()->addFile(PATH_APP_CODE.$module->find('codePool')->getValue().$path);
					}
				}
			}
		}

		// Load the files
		$this->getCollection()->load();
	}

}
?>